package com.dc.schedule.server.service.server.impl;

import cn.hutool.core.net.NetUtil;
import com.dc.schedule.server.base.TransactionalExecutor;
import com.dc.schedule.server.config.ServerConfig;
import com.dc.schedule.server.domain.ClientRegistryEntity;
import com.dc.schedule.server.domain.ServerRegistryEntity;
import com.dc.schedule.server.repository.ClientRegistryRepository;
import com.dc.schedule.server.repository.ServerRegistryRepository;
import com.dc.schedule.server.service.server.ServerRegistryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>Descriptions...
 *
 * @author Diamon.Cheng
 * @date 2022/7/26.
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ServerRegistryServiceImpl implements ServerRegistryService, InitializingBean, DisposableBean {
    private final ServerConfig serverConfig;
    private final ServerRegistryRepository serverRegistryRepository;
    private final ClientRegistryRepository clientRegistryRepository;
    private final TransactionalExecutor transactionalExecutor;
    private ServerRegistryEntity serverRegistryEntity;
    
    @Override
    public ServerRegistryEntity getCurrentServerRegistry() {
        return serverRegistryEntity;
    }
    
    @Override
    public void afterPropertiesSet() {
        transactionalExecutor.executeRunnable(this::registryServer);
    }
    
    @Value("${server.port:8080}")
    private int serverPort;
    @Value("${server.servlet.context-path:/}")
    private String contextPath;
    
    private String serverWebEndpoint() {
        if (StringUtils.hasText(serverConfig.getAccessUrl())) {
            return serverConfig.getAccessUrl();
        }
        final String localhostAddress = NetUtil.getLocalhost().getHostAddress();
        if (localhostAddress == null) {
            throw new IllegalStateException("[服务端]无法找到本机地址, 请配置 schedule.server.access-url");
        }
        return "http://" + localhostAddress + ":" + serverPort + contextPath;
    }
    
    protected LocalDateTime getNowLocalDateTime() {
        return LocalDateTime.now();
    }
    
    private void registryServer() {
        try {
            ServerRegistryEntity toInsert = new ServerRegistryEntity();
            toInsert.setServerId(serverConfig.getServerId());
            toInsert.setClientRegistries(new ArrayList<>(4));
            serverRegistryEntity =
                    transactionalExecutor.execute(() -> serverRegistryRepository.save(toInsert));
        } catch (DataIntegrityViolationException e) {
            log.warn("server Id [" + serverConfig.getServerId() + "] 已经存在, 直接使用该serverId");
            serverRegistryEntity = serverRegistryRepository.getByServerId(serverConfig.getServerId());
        } catch (RuntimeException ex) {
            throw ex;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        serverRegistryEntity.setServerWebEndpoint(this.serverWebEndpoint());
        serverRegistryEntity.setHeartbeatTime(getNowLocalDateTime());
        serverRegistryRepository.save(serverRegistryEntity);
        final List<ClientRegistryEntity> toDelete = new ArrayList<>(serverRegistryEntity.getClientRegistries());
        for (ClientRegistryEntity client : serverRegistryEntity.getClientRegistries()) {
            client.getStaticTasks().clear();
        }
        clientRegistryRepository.deleteAll(toDelete);
        serverRegistryEntity.getClientRegistries().clear();
        
    }
    
    @Scheduled(
            cron = "0/${schedule.server.heartbeat-rate-seconds:5} * * * * ? "
    )
    public void refreshServerHeartbeatTime() {
        log.debug("Server Registry refreshServerHeartbeatTime Start");
        transactionalExecutor.executeRunnable(() -> {
            serverRegistryEntity =
                    serverRegistryRepository.findById(serverRegistryEntity.getId())
                            .orElseThrow(() -> new IllegalArgumentException(
                                    "Server Registry [" + serverRegistryEntity.getServerId() + "] 被违规删除?"));
            serverRegistryEntity.setHeartbeatTime(getNowLocalDateTime());
        });
        log.debug("Server Registry refreshServerHeartbeatTime End");
    }
    
    @Override
    public void destroy() {
        transactionalExecutor.executeRunnable(() -> {
            final List<ClientRegistryEntity> toDelete = new ArrayList<>(serverRegistryEntity.getClientRegistries());
            serverRegistryEntity.getClientRegistries().forEach(client -> {
                client.getStaticTasks().clear();
            });
            serverRegistryEntity.getClientRegistries().clear();
            clientRegistryRepository.deleteAll(toDelete);
            serverRegistryRepository.delete(serverRegistryEntity);
        });
    }
    
}
